home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 January: Mac OS SDK / Dev.CD Jan 99 SDK1.toast / Development Kits / Apple Guide / Engineering / APISample / APISampleMPW / Source / UAppMo.cp < prev    next >
Encoding:
Text File  |  1994-07-25  |  29.7 KB  |  1,093 lines  |  [TEXT/MPS ]

  1. // Copyright ©1994 Apple Computer, Inc.
  2. // Author: John Powers
  3. // Date:   09-Jun-94
  4.  
  5. // UAppMo.cp
  6. // The derived application class for MoGuide.
  7.  
  8. // Revision history (most recent first):
  9. //
  10. // 09-Jun-94 1.0d2e1 JRP
  11. // Add check for valid art document in TAppMo::ReplyToCoach.
  12. // 17-Mar-94 1.0d1e5 JRP
  13. // First public release (develop issue 18).
  14. //
  15.  
  16. #ifndef __UAPPMO__
  17.     #include "UAppMo.h"
  18. #endif
  19.  
  20. #include <Dialogs.h>
  21.  
  22.         // Segment
  23.  
  24. #pragma segment Main
  25.  
  26. // ------------------------------------------------------------------------
  27. // TAppMo::HandleAECustom
  28. // Handles the custom events.
  29. // The refCon contains our application object.
  30. // Should be in a locked, unpurgeable segment.
  31. //
  32. // Unfortunately, the DTS sample TApplication does not process
  33. // high level events.  We fix that by overriding the EventLoop
  34. // and adding high-level event processing.
  35. //
  36. pascal OSErr
  37. TAppMo::HandleAECustom(AppleEvent& theAppleEvent,
  38.                         AppleEvent& /*theReply*/, long refCon)
  39. {
  40.     OSType        eventId;
  41.     Size        actualSize;
  42.     DescType    returnedType;
  43.     OSErr        err=noErr;
  44.             // Useless without our application object.
  45.     TAppMo* ourApp=(TAppMo*)refCon;
  46.     if(!ourApp)
  47.         return kErrNoAppObject;
  48.             // Get a potential document collaboarator.
  49.     TDocArt* docArt = ourApp->GetDocArt();
  50.             // Get event id.
  51.     err = AEGetAttributePtr(&theAppleEvent, keyEventIDAttr,
  52.                                 typeType, &returnedType,
  53.                                 (Ptr) &eventId, sizeof(eventId),
  54.                                 &actualSize);
  55.     switch (eventId)
  56.     {
  57.         case kAEIdCloseDocArt:
  58.             ourApp->CloseDoc(ourApp->fDocArt);
  59.             break;
  60.         case kAEIdShowDocArt:
  61.             ourApp->ShowArt();
  62.             ourApp->DoAdvance();
  63.             break;
  64.         case kAEIdCloseDocFB:
  65.             ourApp->CloseDoc(ourApp->fDocFB);
  66.             break;
  67.         case kAEIdShowDocFB:
  68.             ourApp->ShowFeedback();
  69.             break;
  70.         case kAEIdReset:
  71.             if(docArt)
  72.                 docArt->SetWantReset();
  73.             break;
  74.         case kAEIdShuffle:
  75.             if(docArt)
  76.                 docArt->SetWantShuffle();
  77.             break;
  78.         case kAEIdQuitApp:
  79.             ourApp->ExitLoop();
  80.             break;
  81.         case kAEIdWaitCollision:
  82.                 // The context check will set the event.
  83.                 // If the event is constant, we could set it
  84.                 // right here and skip the context check.
  85.                 // Otherwise, we need the context check to
  86.                 // send us the event that the author desires.
  87.             if(docArt)
  88.                 docArt->SetWantCollision(true);
  89.             break;
  90.         case kAEIdIgnoreCollision:
  91.                 // Ignore any collision,
  92.                 // turn off flag and clear event.
  93.             if(docArt)
  94.             {
  95.                 docArt->SetWantCollision(false);
  96.                 docArt->SetCollisionEvent(0);
  97.             }
  98.             break;
  99.         case kAEIdAdvance:
  100.                 // Advance on the next open-window event.
  101.             ourApp->SetOkayAdvance(true);
  102.             break;
  103.         default:
  104.             err = errAEEventNotHandled;
  105.             break;
  106.     }
  107.     if(ourApp->fFBEvent)
  108.         ourApp->fFBEvent->SetFlash(eventId);
  109.     return err;
  110.     
  111. }
  112.  
  113. // --------------------------------------------------------------------
  114. // TAppMo::ReplyToCoach
  115. //
  116. // This is a callback invoked by the Coach Apple event handler.
  117. // This member function and the refCon
  118. // was provided by us in the Init call.
  119. //
  120. // Handles replies for Coach queries sent to OurApp.
  121. // Called with the item name and refCon.
  122. // Return the item Rect and a result code.
  123. //
  124. // The name is a zero-terminated string (C-string).
  125. //
  126. // Caveats:
  127. //
  128. //        Our app resource map is not available.
  129. //        If you need a resource such as a string,
  130. //        read it in advance and save it in memory.
  131. //
  132. // A static function in main segment.
  133. pascal OSErr
  134. TAppMo::ReplyToCoach(Rect* pItemRect, Ptr pName, long refCon)
  135. {
  136.     OSErr        result=kAGErrItemNotFound;
  137.             // We probably need an A5 world.
  138.     long restoreGlobals = SetA5(*(long*)CurrentA5);
  139.             // Our application object is in the refcon.
  140.     TAppMo* ourApp=(TAppMo*) refCon;
  141.     if(ourApp)
  142.     {
  143.             // Get art document.
  144.         TDocArt* docArt = ourApp->fDocArt;
  145.             // Make sure we have a valid document (may be closed).
  146.         if(docArt)
  147.         {
  148.                 // Ask our document window for the rect of the named item.
  149.             if(docArt->GetLocation(pName, pItemRect))
  150.             {
  151.                 GrafPtr    savePort;
  152.                     // Return the rectangle in global coordinates
  153.                 GetPort(&savePort);
  154.                     // Get the GrafPort.
  155.                 SetPort((GrafPtr) ourApp->fDocArt->GetDocWindow());
  156.                     // Top-left
  157.                 LocalToGlobal((Point*)&(pItemRect->top));
  158.                     // Bottom-right
  159.                 LocalToGlobal((Point*)&(pItemRect->bottom));
  160.                 SetPort(savePort);
  161.                 result = noErr;
  162.             }
  163.         }
  164.             // Feedback - auditory and visual
  165.         if(ourApp->fFBCoach)
  166.             ourApp->fFBCoach->SetFlash(pName);
  167.     } // if(ourApp…
  168.      SetA5(restoreGlobals);
  169.     return(result);
  170. }
  171.  
  172. // ------------------------------------------------------------------------
  173. // TContext::ReplyToContext
  174. // Handles replies for context checks sent to OurApp.
  175. // Called with the data in the context check database object
  176. // and the event you sent in the Init.
  177. // Return the result (true or false).
  178. // A callback.
  179. // A static function in main segment.
  180. pascal OSErr
  181. TContext::ReplyToContext(Ptr pInput, Size inputDataSize,
  182.                             Ptr *ppOutput, Size *pOutputDataSize,
  183.                             AGAppInfoHdl hAppInfo)
  184. {
  185.     OSErr        err=noErr;
  186.     long        inputValue=0;
  187.     short        result=false;
  188.             // We probably need an A5 world.
  189.     long restoreGlobals = SetA5(*(long*)CurrentA5);
  190.             // The input value can be used by either event id.
  191.     if(pInput && inputDataSize>0)
  192.     {
  193.         inputValue = *((long*)pInput);
  194.     }
  195.             // Our application object was passed by us in the refCon.
  196.             // It is returned to us in the refcon of hAppInfo.
  197.     TAppMo* ourApp = (TAppMo*) (**hAppInfo).refCon;
  198.             // Which event id?
  199.     switch ((**hAppInfo).eventId)
  200.     {
  201.                     // kAEIdContextCollision
  202.                     // Set the collision event to the inputValue.
  203.                     // When a collision occurs, the event will
  204.                     // be sent to Apple Guide.  In other words,
  205.                     // Apple Guide is saying "Let me know when
  206.                     // a collision occurs by sending me this event."
  207.                     // A zero input value cancels the event.
  208.                     // Always returns a false result.
  209.         case kAEIdContextCollision:
  210.             if(ourApp->fDocArt)
  211.                 if(ourApp->fDocArt->WantCollision())
  212.                     ourApp->fDocArt->SetCollisionEvent(inputValue);
  213.             result = false;
  214.             break;
  215.                     // kAEIdContextBeep
  216.                     // Return the value (true or false) saved in fBeepReturn.
  217.         case kAEIdContextBeep:
  218.             result = ourApp->fBeepReturn;
  219.             break;
  220.                     // kAEIdContextWindow
  221.                     // Return true if a document window is open and showing.
  222.         case kAEIdContextWindow:
  223.             result = (ourApp->fDocArt)?ourApp->fDocArt->IsWindowVisible():false;
  224.             break;
  225.         default:
  226.                     // An unknown event will be ignored.
  227.             break;
  228.     }
  229.         // Create storage for our result.
  230.     *ppOutput = NewPtr(sizeof(short));
  231.     if(*ppOutput)
  232.     {
  233.             // Set the result and its size.
  234.         **ppOutput = result;
  235.         *pOutputDataSize = sizeof(short);
  236.     }
  237.         // Feedback - auditory and visual
  238.     if(ourApp->fFBContext)
  239.         ourApp->fFBContext->SetFlash((**hAppInfo).eventId);
  240.  
  241.      SetA5(restoreGlobals);
  242.     return err;
  243. }
  244.  
  245.         // Segment
  246.  
  247. #pragma segment MoG1
  248.  
  249. // =========================================================================
  250. // TAppMo
  251. // ------------------------------------------------------------------------
  252. TAppMo::TAppMo()
  253. {
  254. };
  255.  
  256. // ------------------------------------------------------------------------
  257. // TAppMo::AddGuideFiles
  258. // Add the guide files to the mMoGuide menu.
  259. //
  260. // If there are any files, a dashed line is added first,
  261. // followed by the menu name for each file.  We save
  262. // the FSSpec's for easy retrieval when the user chooses
  263. // the menu item.  If any guide files already exist on
  264. // the menu, they are deleted first and a fresh list
  265. // is prepared.
  266. //
  267. void
  268. TAppMo::AddGuideFiles()
  269. {
  270.     short        newFileCount;
  271.     Str63        menuName;
  272.     FSSpec        fileSpec;
  273.     MenuHandle    hmMoGuide = GetMHandle(mMoGuide);
  274.     if(hmMoGuide)
  275.     {
  276.             // If we already have a file list, delete it.
  277.         if(this->fhFileList)
  278.         {
  279.                 // We do, dispose of it and its menu items.
  280.             DisposeHandle(this->fhFileList);
  281.             while(this->fFileCount--)
  282.             {
  283.                     // Delete the item after the dash line.
  284.                 DelMenuItem(hmMoGuide, iLastMenuItem+2);
  285.             }
  286.                 // Delete dashed line.
  287.             DelMenuItem(hmMoGuide, iLastMenuItem+1);
  288.         }
  289.             // Set the search directory for guide files.
  290.         short    vRefNum = (-*(short*)SFSaveDisk);
  291.         long    dirID = (*(long*)CurDirStore);
  292.         Boolean    wantMixin = false;
  293.         newFileCount = AGFileGetDBCount(vRefNum, dirID,
  294.                                         kAGFileDBTypeAny, wantMixin);
  295.                                     
  296.         if(newFileCount>0)
  297.         {
  298.                 // Create a new list of file FSSpecs.
  299.             this->fhFileList = NewHandle(newFileCount*sizeof(FSSpec));
  300.             if(this->fhFileList)
  301.             {
  302.                     // Add dashed line.
  303.                 AppendMenu(hmMoGuide, "\p-");
  304.                     // Get each file, add to menu and to list.
  305.                 this->fFileCount = newFileCount;
  306.                 for(short i=1; i<=newFileCount; i++)
  307.                 {
  308.                         // Use same vRefNum, dirID, and wantMixin
  309.                         // that was set for the count.
  310.                     if(AGFileGetIndDB(vRefNum, dirID,
  311.                                     kAGFileDBTypeAny, wantMixin,
  312.                                     i, &fileSpec)==noErr)
  313.                     {
  314.                         if(AGFileGetDBMenuName(&fileSpec, menuName)==noErr)
  315.                         {
  316.                                 // We have a guide file,
  317.                                 // get it's menu name and add to menu.
  318.                             AppendMenu(hmMoGuide, menuName);
  319.                             ((FSSpec*)(*this->fhFileList))[i-1] = fileSpec;
  320.                         }
  321.                     }
  322.                 } // for(short…
  323.             } // if(hFileMenu…
  324.         } // if(newFileCount…
  325.     } // if(hmMoGuide…
  326. }
  327.  
  328. // --------------------------------------------------------------------------
  329. // TAppMo::AdjustMenus
  330. //
  331. void
  332. TAppMo::AdjustMenus()            // override
  333. {
  334.             // Inherit first.
  335.     TApp::AdjustMenus();
  336.             // Our menus.
  337.     MenuHandle    hmMoGuide=GetMHandle(mMoGuide);
  338.     MenuHandle    hmWindow=GetMHandle(mWindow);
  339.             // Always enabled
  340.     EnableItem(hmMoGuide, iFiles);
  341.             // Set default case.
  342.     DisableItem(hmMoGuide, iStartGuide);
  343.     DisableItem(hmMoGuide, iQuitGuide);
  344.     DisableItem(hmMoGuide, iOpenDefault);
  345.     DisableItem(hmMoGuide, iClose);
  346.             // If Apple Guide is installed, then we can do lots of things.
  347.     if(gAGuideAvailable)
  348.     {
  349.         AGStatus status = AGGetStatus();
  350.                 // Permit starting it if it's not already started.
  351.         if(status==kAGIsNotRunning)
  352.             EnableItem(hmMoGuide, iStartGuide);
  353.                 // If Apple Guide is sleeping, then we can make it quit.
  354.         else if(status==kAGIsSleeping)
  355.             EnableItem(hmMoGuide, iQuitGuide);
  356.                 // If we have default help available, then we can open it.
  357.         if(AGGetAvailableDBTypes() & kAGDBTypeBitHelp)
  358.             EnableItem(hmMoGuide, iOpenDefault);
  359.                 // If we have a non-nil this->fGuideRefNum,
  360.                 // then a database is open. Permit closing.
  361.                 // this->fGuideRefNum is tracked in TApp::AdjustMenus.
  362.         if(this->fGuideRefNum)
  363.             EnableItem(hmMoGuide, iClose);
  364.                 // Enable any databases that we may have added.
  365.         for(short i=0; i<this->fFileCount; i++)
  366.             EnableItem(hmMoGuide, iLastMenuItem+i+2);
  367.     }
  368.             // Always available.
  369.             // Art-related items.
  370.     EnableItem(hmWindow, iWinArt);
  371.         // Can reset objects if a window is visible.
  372.     if(this->fDocArt)
  373.     {
  374.         EnableItem(hmWindow, iResetArt);
  375.         EnableItem(hmWindow, iShuffleArt);
  376.     }
  377.     else
  378.     {
  379.         DisableItem(hmWindow, iResetArt);
  380.         DisableItem(hmWindow, iShuffleArt);
  381.     }
  382.             // Always available.
  383.     EnableItem(hmWindow, iFeedback);
  384.     EnableItem(hmWindow, iContextTrue);
  385.     CheckItem(hmWindow, iContextTrue, this->fBeepReturn);
  386.     EnableItem(hmWindow, iContextBeep);
  387.     if(this->fFBContext)
  388.         CheckItem(hmWindow, iContextBeep, this->fFBContext->GetBeep());
  389.  
  390. }
  391.  
  392. // ------------------------------------------------------------------------
  393. // TAppMo::CheckFrontWindow
  394. // Check to see if Apple Guide has a window showing.
  395. // Update the miscellaneous feedback with the result.
  396. // We look for the front window of the current database
  397. // which is not necessarily our own. If we wanted just
  398. // our own database, we would pass this->fGuideRefNum
  399. // instead of kAGFrontDatabase.
  400. //
  401. void
  402. TAppMo::CheckFrontWindow()
  403. {
  404.     Str255 string;
  405.     short index=kStrNone;
  406.     if(gAGuideAvailable)
  407.     {
  408.             // If Apple Guide is available, then the choices
  409.             // are nothing (Apple Guide is not running),
  410.             // sleeping, access window, or presentation window.
  411.         if(AGGetStatus()!=kAGIsNotRunning)
  412.         {
  413.             AGWindowKind kind = AGGetFrontWindowKind(kAGFrontDatabase);
  414.                 // Clarity, not conciseness.
  415.             if(kind==kAGNoWindow)
  416.                 index = kStrNoneSleeping;
  417.             else if(kind==kAGAccessWindow)
  418.                 index = kStrAccess;
  419.             else if(kind==kAGPresentationWindow)
  420.                 index = kStrPresentation;
  421.         }
  422.     }
  423.         // Update FBMisc if there is a change.
  424.         // It flashes every time we update it.
  425.         // We force an update every kMaxStatusUpdateCnt cycles.
  426.     this->fLastStatusCnt--;
  427.     if(this->fLastStatusCnt==0 || index!=this->fLastMiscIndex)
  428.     {
  429.         GetIndString(string, kFrontWindowStrId, index);
  430.             // If we are in the idle loop, we must check this->fFBMisc
  431.             // validity in case it hasn't been created yet.
  432.         if(this->fFBMisc)
  433.         {
  434.             this->fFBMisc->SetFlash(string);
  435.             this->fLastMiscIndex = index;
  436.         }
  437.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  438.     }
  439. }
  440.  
  441. // ------------------------------------------------------------------------
  442. // TAppMo::DoAdvance
  443. // If an advance is wanted, do it.
  444. // This is a one-shot event.
  445. // We send a go-back event because we assume that Apple Guide
  446. // is showing an oops panel.
  447. void
  448. TAppMo::DoAdvance()
  449. {
  450.     if(gAGuideAvailable && this->fOkayAdvance)
  451.     {
  452.         AlertIfError(AGGeneral(this->fGuideRefNum, kAGEventReturnBack));
  453.     }
  454.     this->fOkayAdvance = false;
  455. }
  456.  
  457. // ------------------------------------------------------------------------
  458. // TAppMo::DoGoAway
  459. //
  460. // This is the close side of TAppMo::ShowArt, ShowClipboard, ShowFeedback.
  461. //
  462. void
  463. TAppMo::DoGoAway()
  464. {
  465.             // Clear our local document record if
  466.             // it's object is going away.
  467.     if(this->fCurDoc==this->fDocFB)
  468.     {
  469.         this->fDocFB = nil;
  470.         this->fFBContext->SetDoc(nil);
  471.         this->fFBCoach->SetDoc(nil);
  472.         this->fFBEvent->SetDoc(nil);
  473.         this->fFBMisc->SetDoc(nil);
  474.     }
  475.     else if(this->fCurDoc==this->fDocArt)
  476.         this->fDocArt = nil;
  477.             // Inherit go-away action from TApp and TApplication
  478.     TApp::DoGoAway();
  479. }
  480.  
  481. // ------------------------------------------------------------------------
  482. // TAppMo::DoIdle
  483. // Do our idle and deferred events.
  484. //
  485. void
  486. TAppMo::DoIdle()        // override
  487. {
  488.         // Inherit to do TApp Idle.
  489.     TApp::DoIdle();
  490.         // Document actions.
  491.     if(this->fDocArt)
  492.         this->fDocArt->DoIdle();
  493.         // Feedback about context-checking.
  494.     if(this->fFBContext)
  495.         this->fFBContext->DoIdle();
  496.         // Feedback about Coach objects.
  497.     if(this->fFBCoach)
  498.         this->fFBCoach->DoIdle();
  499.         // Feedback about Apple events.
  500.     if(this->fFBEvent)
  501.         this->fFBEvent->DoIdle();
  502.         // Check for Apple Guide front window.
  503.     this->CheckFrontWindow();
  504.         // Miscellaneous feedback.
  505.     if(this->fFBMisc)
  506.         this->fFBMisc->DoIdle();
  507. }
  508.  
  509. // ------------------------------------------------------------------------
  510. // TAppMo::DoMenuCommand
  511. // This is called when an item is chosen from the menu bar (after calling
  512. // MenuSelect or MenuKey). It does the right thing for each command.
  513. //
  514. // TAppMo::AdjustMenus enables the menu items
  515. // depending on whether or not guide is available.
  516. // Consequently, we don't have to check here to see
  517. // if guide is available because, if it isn't, the item
  518. // won't be choosen.
  519. //
  520. void
  521. TAppMo::DoMenuCommand(short menuID, short menuItem)        //  override
  522. {
  523.     FSSpec    fileSpec;
  524.     OSErr    err=noErr;
  525.     switch (menuID)
  526.     {
  527.         case mMoGuide:
  528.             switch(menuItem)
  529.             {
  530.                 case iStartGuide:
  531.                     AlertIfError(AGStart());
  532.                     break;
  533.                 case iQuitGuide:
  534.                     AlertIfError(AGQuit());
  535.                     break;
  536.                 case iOpenDefault:
  537.                     AlertIfError(this->OpenGuideDatabase(kAGDefault));
  538.                     break;
  539.                 case iClose:
  540.                     err = AGClose(&this->fGuideRefNum);
  541.                     if(err==kAGErrDatabaseNotOpen)
  542.                         this->fGuideRefNum = nil;
  543.                     break;
  544.                 case iFiles:
  545.                     this->AddGuideFiles();
  546.                     break;
  547.                 default:
  548.                         // Must be one of the guide file items that we added.
  549.                         // Get its FSSpec from the list and open it.
  550.                     if(this->fhFileList)
  551.                     {
  552.                         fileSpec = ((FSSpec*)(*this->fhFileList))[menuItem-iLastMenuItem-2];
  553.                         AlertIfError(this->OpenGuideDatabase(&fileSpec));
  554.                     }
  555.                     break;
  556.             } // switch
  557.             break;
  558.         case mWindow:
  559.             switch(menuItem)
  560.             {
  561.                 case iWinArt:
  562.                     err = this->SendEventToSelf(kAEIdShowDocArt);
  563.                     break;
  564.                 case iFeedback:
  565.                     err = this->SendEventToSelf(kAEIdShowDocFB);
  566.                     break;
  567.                 case iContextTrue:
  568.                     this->fBeepReturn = !this->fBeepReturn;
  569.                     break;
  570.                 case iContextBeep:
  571.                     if(this->fFBContext)
  572.                         this->fFBContext->SetBeep(!this->fFBContext->GetBeep());
  573.                     break;
  574.                 case iResetArt:
  575.                     err = this->SendEventToSelf(kAEIdReset);
  576.                     break;
  577.                 case iShuffleArt:
  578.                     err = this->SendEventToSelf(kAEIdShuffle);
  579.                     break;
  580.                 default:
  581.                     break;
  582.             } // switch
  583.             break;
  584.         default:
  585.             break;
  586.     } // switch
  587.             // Inherit for the Apple, File, and Edit menus.
  588.     TApp::DoMenuCommand(menuID, menuItem);
  589.     AlertIfError(err);
  590. } // DoMenuCommand
  591.  
  592. // ---------------------------------------------------------------------
  593. // TAppMo::Init
  594. // Do the things that our derived application class requires.
  595. // Basic initialization, before we start.
  596. // We store our application object in the core event handler
  597. // refCon so that the handler can use it.  Avoids making it a global.
  598. // Return noErr if successful.
  599. OSErr
  600. TAppMo::Init()
  601. {
  602.             // Inherit
  603.     OSErr err = TApp::Init();
  604.     if(err==noErr)
  605.     {
  606.             // Override the menubar ID.
  607.         this->fMenuBarID = rMenuBarMo;
  608.             // Clear our collaborators because we'll not want to use
  609.             // them until after we get the core startup event.
  610.                 // Clear handler refNums.
  611.         this->fCoachRefNum = nil;
  612.         this->fContext = nil;
  613.         this->fFBContext = nil;
  614.         this->fFBCoach = nil;
  615.         this->fFBEvent = nil;
  616.         this->fFBMisc = nil;
  617.         this->fDocArt = nil;
  618.         this->fDocFB = nil;
  619.             // Also clear our variables or we may get a bus error
  620.             // at the first DoIdle (before we get the start event.)
  621.         this->fhFileList = nil;
  622.         this->fFileCount = 0;
  623.         this->fBeepReturn = false;
  624.         this->fOkayAdvance = false;
  625.         this->fLastMiscIndex = 0;
  626.             // Remove the core event handler installed in TApp.
  627.         (void) AERemoveEventHandler(kCoreEventClass,
  628.                                 typeWildCard,
  629.                                 kHandlerNotRequired,
  630.                                 kIsNotSysHandler);
  631.             // Replace with a core event handler which
  632.             // uses the same handler, but passes TAppMo
  633.             // in the refCon instead of TApp.  This
  634.             // insures that TAppMo::Start is invoked.
  635.         err = AEInstallEventHandler(kCoreEventClass,
  636.                                 typeWildCard,
  637.                                 (EventHandlerProcPtr) TApp::HandleAECore,
  638.                                 (long)this,
  639.                                 kIsNotSysHandler);
  640.             // Install our custom event handler.
  641.             // We put the TAppMo object in the refCon.
  642.         err = AEInstallEventHandler(kAEClassCustom,
  643.                                 typeWildCard,
  644.                                 (EventHandlerProcPtr) TAppMo::HandleAECustom,
  645.                                 (long)this,
  646.                                 kIsNotSysHandler);
  647.             // Initialize our status update cycle counter.
  648.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  649.             // Starting is done in TApp::HandleAECore.
  650.             // We wait until that happens before proceeding.
  651.     }
  652.     return err;
  653. }
  654.  
  655. // ------------------------------------------------------------------------
  656. // TAppMo::Quit
  657. // We're quitting, so delete everything.
  658. // All of this probably goes away in the application heap anyway,
  659. // we're just being compulsively tidy.
  660. //
  661. void
  662. TAppMo::Quit()
  663. {
  664.     if(gAGuideAvailable)
  665.     {
  666.                 // Remove Coach and context handlers.
  667.         AlertIfError(AGRemoveCoachHandler(&this->fCoachRefNum));
  668.     }
  669.             // Delete our context object.
  670.     if(this->fContext)
  671.     {
  672.         AlertIfError(this->fContext->Remove());
  673.         delete this->fContext;
  674.     }
  675.             // Delete our flasher objects.
  676.     if(this->fFBContext)
  677.         delete this->fFBContext;
  678.     if(this->fFBCoach)
  679.         delete this->fFBCoach;
  680.     if(this->fFBEvent)
  681.         delete this->fFBEvent;
  682.     if(this->fFBMisc)
  683.         delete this->fFBMisc;
  684.             // Remove list of guide files.
  685.     if(this->fhFileList)
  686.         DisposeHandle(this->fhFileList);
  687.             // Remove custom event handler.
  688.     (void) AERemoveEventHandler(kAEClassCustom,
  689.                                 typeWildCard,
  690.                                 kHandlerNotRequired,
  691.                                 kIsNotSysHandler);
  692.             // Inherit.
  693.     TApp::Quit();
  694. }
  695.  
  696. // ------------------------------------------------------------------------
  697. // TAppMo::ShowArt
  698. // Opens the art window.
  699. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  700. //
  701. // The art is specified in a resource.
  702. // The art resource id matches the id of its window.
  703. // To add another window, specify a new resource id,
  704. // define the new art resource, and instantiate a new TDocArt.
  705. //
  706. void
  707. TAppMo::ShowArt()
  708. {
  709.     if(this->fDocArt==nil)
  710.     {
  711.             // Art window is not present.
  712.             // Create a document and window for the art.
  713.         this->fDocArt = new TDocArt(kArt1WindResID);
  714.         if(this->fDocArt)
  715.         {
  716.                 // Add new document to document list.
  717.             this->fDocList->AddDoc((TDocument*) this->fDocArt);
  718.                 // Update current document and window pointer.
  719.             this->fCurDoc = this->fDocArt;
  720.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  721.             SetPort(this->fWhichWindow);
  722.                 // Initialize art document window.
  723.             AlertIfError(this->fDocArt->Init(kArt1WindResID));
  724.                 // Set the application object as a collaborator.
  725.             this->fDocArt->SetApp(this);
  726.         }
  727.     }
  728.         // Better show it, or at least bring it to the front.
  729.     this->fDocArt->Show();
  730.             // Invalidate window so that it will be updated and drawn.
  731.     this->fDocArt->Invalidate();
  732. }
  733.  
  734. // ------------------------------------------------------------------------
  735. // TAppMo::ShowFeedback
  736. // Opens the feedback window.
  737. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  738. //
  739. void
  740. TAppMo::ShowFeedback()
  741. {
  742.     if(this->fDocFB==nil)
  743.     {
  744.             // Feedback window is not present.
  745.             // Create a document and window for the feedback.
  746.         this->fDocFB = new TDocFB(kFeedbackWinResID);
  747.         if(this->fDocFB)
  748.         {
  749.                 // Add new document to document list.
  750.             this->fDocList->AddDoc((TDocument*) this->fDocFB);
  751.                 // Update current document and window pointer.
  752.             this->fCurDoc = this->fDocFB;
  753.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  754.             SetPort(this->fWhichWindow);
  755.                     // Initialize feedback document window.
  756.             AlertIfError(this->fDocFB->Init());
  757.                 // Set the application object as a collaborator.
  758.             this->fDocFB->SetApp(this);
  759.                 // Set document window collaborator for feedback.
  760.             this->fFBContext->SetDoc(this->fDocFB);
  761.             this->fFBCoach->SetDoc(this->fDocFB);
  762.             this->fFBEvent->SetDoc(this->fDocFB);
  763.             this->fFBMisc->SetDoc(this->fDocFB);
  764.         }
  765.     }
  766.         // Better show it, or at least bring it to the front.
  767.     this->fDocFB->Show();
  768.             // Invalidate window so that it will be updated and drawn.
  769.     this->fDocFB->Invalidate();
  770. }
  771.  
  772. // ---------------------------------------------------------------------
  773. // TAppMo::Start
  774. // Startup the application.
  775. // The Init is done in main.  The Start comes with the oapp or odoc event.
  776. // Return noErr if successful.
  777. OSErr
  778. TAppMo::Start()
  779. {
  780.             // Inherit.
  781.     OSErr err = TApp::Start();
  782.     {
  783.                 // Reply to Coach mark queries.
  784.                 // Pass along the TApp object.
  785.                 // Setup our callbacks for context-check queries.
  786.                 // Pass along the TApp object in the refCon.
  787.         if(gAGuideAvailable)
  788.         {
  789.             err = AGInstallCoachHandler((CoachReplyProcPtr)TAppMo::ReplyToCoach,
  790.                                         (long)this, &this->fCoachRefNum);
  791.             if(err!=noErr)
  792.                 return err;
  793.             this->fContext = new TContext;
  794.             if(this->fContext)
  795.                 err = this->fContext->Install((long)this);
  796.             else
  797.                 err = kErrNoContextObject;
  798.             if(err!=noErr)
  799.                 return err;
  800.         }
  801.                 // List of guide files.
  802.         this->fhFileList = nil;
  803.         this->fFileCount = 0;
  804.                 // Don't want a panel advance yet.
  805.         this->fOkayAdvance = false;
  806.                 // Return a true or false to the beep context check query?
  807.         this->fBeepReturn = false;
  808.                 // Feedback (the Flasher).
  809.         this->fFBContext = new TFeedback;
  810.         if(this->fFBContext)
  811.             this->fFBContext->Init(this, dataCC);
  812.         this->fFBCoach = new TFeedback;
  813.         if(this->fFBCoach)
  814.             this->fFBCoach->Init(this, dataCH);
  815.         this->fFBEvent = new TFeedback;
  816.         if(this->fFBEvent)
  817.             this->fFBEvent->Init(this, dataEV);
  818.         this->fFBMisc = new TFeedback;
  819.         if(this->fFBMisc)
  820.         {
  821.             this->fFBMisc->Init(this, dataTP);
  822.                 // Set an infinite interval for FBMisc.
  823.             this->fFBMisc->SetInterval(0);
  824.         }
  825.                 // Show the feedback window/document.
  826.         err = this->SendEventToSelf(kAEIdShowDocFB);
  827.     }
  828.     return err;
  829. }
  830.  
  831. // =========================================================================
  832. // TContext
  833. // ------------------------------------------------------------------------
  834. TContext::TContext()
  835. {
  836.     this->fHandlerCnt = 0;
  837. }
  838.  
  839. // ------------------------------------------------------------------------
  840. TContext::~TContext()
  841. {
  842.         // If we still have handlers installed, remove them.
  843.     if(this->fHandlerCnt>0)
  844.         (void) this->Remove();
  845. }
  846.  
  847. // ------------------------------------------------------------------------
  848. // TContext::Install
  849. // Install context-check handlers.
  850. // The appObj is the application object
  851. // used in the reply to access the application.
  852. // If any error occurs, return it.
  853. //
  854. OSErr
  855. TContext::Install(long appObj)
  856. {
  857.     OSErr result=noErr;
  858.     OSErr eachResult;
  859.         // Define and install context-check handlers.
  860.         // If we still have handlers installed, remove them.
  861.     if(this->fHandlerCnt>0)
  862.         result = this->Remove();
  863.     this->fHandlerCnt = kContextHandlerCnt;
  864.     this->fContext[0].eventID = kAEIdContextCollision;
  865.     this->fContext[1].eventID = kAEIdContextBeep;
  866.     this->fContext[2].eventID = kAEIdContextWindow;
  867.     for(short i=0; i<this->fHandlerCnt; i++)
  868.     {
  869.             // Install handler.
  870.             // If an error, the refNum will be set to nil by Apple Guide.
  871.         eachResult = AGInstallContextHandler(
  872.                                         (ContextReplyProcPtr)TContext::ReplyToContext,
  873.                                         this->fContext[i].eventID,
  874.                                         appObj,
  875.                                         &this->fContext[i].refNum);
  876.             // Track only errors in result.
  877.         result = (eachResult!=noErr)?eachResult:result;
  878.     }
  879.     return result;
  880. }
  881.  
  882. // ------------------------------------------------------------------------
  883. // TContext::Remove
  884. // Remove context-check handlers.
  885. // If any error occurs, return it.
  886. //
  887. OSErr
  888. TContext::Remove()
  889. {
  890.     OSErr result=noErr;
  891.     OSErr eachResult;
  892.     for(short i=0; i<this->fHandlerCnt; i++)
  893.     {
  894.         eachResult = AGRemoveContextHandler(&this->fContext[i].refNum);
  895.             // Track only errors in result.
  896.         result = (eachResult!=noErr)?eachResult:result;
  897.     }
  898.     this->fHandlerCnt = 0;
  899.     return result;
  900. }
  901.  
  902. // =========================================================================
  903. // TTypeStr
  904. // ------------------------------------------------------------------------
  905. TTypeStr::TTypeStr(OSType theType)
  906. {
  907.     TypeToChar toConvert;
  908.     toConvert.type = theType;
  909.             // Convert stored OSType to pstring.
  910.             // Replace zeros with spaces.
  911.     this->fString[0] = 4;
  912.     for(short i=0; i<4; i++)
  913.         this->fString[i+1] = (toConvert.ch[i])?toConvert.ch[i]:' ';
  914. }
  915.  
  916. // ------------------------------------------------------------------------
  917. // TTypeStr::String
  918. StringPtr
  919. TTypeStr::String()
  920. {
  921.     return &this->fString[0];
  922. }
  923.  
  924. // =========================================================================
  925. // TFeedback
  926. // ------------------------------------------------------------------------
  927. TFeedback::TFeedback()
  928. {
  929.     this->fShowTime = 0;
  930.     this->fInterval = kIntervalShow;
  931.     this->fCurrent = 0;
  932.     this->fPrevious = 0;
  933.     this->fShow = false;
  934.     this->fBeep = false;
  935.     this->fOurApp = nil;
  936.     this->fDocFB = nil;
  937. }
  938.  
  939. // ------------------------------------------------------------------------
  940. TFeedback::~TFeedback()
  941. {
  942. }
  943.  
  944. // ------------------------------------------------------------------------
  945. // TFeedback::DoIdle
  946. // Do any action required during the idle processing.
  947. //
  948. void
  949. TFeedback::DoIdle()
  950. {
  951.     this->Show();
  952. }
  953.  
  954. // ------------------------------------------------------------------------
  955. // TFeedback::Draw
  956. void
  957. TFeedback::Draw(OSType toDraw, short mode)
  958. {
  959.     TTypeStr flashChar = toDraw;
  960.             // Draw it in the feedback window (if present).
  961.     if(this->fDocFB)
  962.         this->fDocFB->DrawData(flashChar.String(), mode, this->fWhichData);
  963. }
  964.  
  965. // ------------------------------------------------------------------------
  966. // TFeedback::GetBeep
  967. // Return the want-beep flag.
  968. Boolean
  969. TFeedback::GetBeep()
  970. {
  971.     return this->fBeep;
  972. }
  973.  
  974. // ------------------------------------------------------------------------
  975. // TFeedback::Init
  976. // Set the application collaborator (and any others you might want to add.)
  977. void
  978. TFeedback::Init(TAppMo* theApp, short whichData)
  979. {
  980.         // The collaborator
  981.     this->fOurApp = theApp;
  982.         // The data
  983.     this->fWhichData = whichData;
  984. }
  985.  
  986. // ------------------------------------------------------------------------
  987. // TFeedback::SetBeep
  988. // Set the want-beep flag.
  989. void
  990. TFeedback::SetBeep(Boolean wantBeep)
  991. {
  992.     this->fBeep = wantBeep;
  993. }
  994.  
  995. // ------------------------------------------------------------------------
  996. // TFeedback::SetFlash
  997. void
  998. TFeedback::SetFlash(OSType theFlash)
  999. {
  1000.     this->fCurrent = theFlash;
  1001.     this->fShow = true;
  1002. }
  1003.  
  1004. // ------------------------------------------------------------------------
  1005. // TFeedback::SetFlash
  1006. // Accept a char ptr, convert its first four characters to an OSType.
  1007. void
  1008. TFeedback::SetFlash(Ptr theFlash)
  1009. {
  1010.     CharToType    flashChar;
  1011.     flashChar.type = '    ';
  1012.     for(short i=0; i<4; i++)
  1013.         if(theFlash[i]!=0)
  1014.             flashChar.ch[i] = theFlash[i];
  1015.     this->fCurrent = flashChar.type;
  1016.     this->fShow = true;
  1017. }
  1018.  
  1019. // ------------------------------------------------------------------------
  1020. // TFeedback::SetFlash
  1021. // Accept a pascal string, convert its first four characters to an OSType.
  1022. void
  1023. TFeedback::SetFlash(Str255 theFlash)
  1024. {
  1025.     short len = (theFlash[0]>4)?4:theFlash[0];
  1026.     CharToType    flashChar;
  1027.     flashChar.type = '    ';
  1028.     for(short i=0; i<len; i++)
  1029.         flashChar.ch[i] = theFlash[i+1];
  1030.     this->fCurrent = flashChar.type;
  1031.     this->fShow = true;
  1032. }
  1033.  
  1034. // ------------------------------------------------------------------------
  1035. // TFeedback::Show
  1036. // Show anything new.
  1037. // When interval is up, erase.
  1038. //
  1039. void
  1040. TFeedback::Show()
  1041. {
  1042.     short drawThisCycle=true;
  1043.     if(this->fShow)
  1044.     {
  1045.         if(this->fPrevious)
  1046.         {
  1047.                 // Something is up there, erase it.
  1048.             this->Draw(this->fPrevious, kErase);
  1049.                 // Is this a repeat of the same item?
  1050.             if(this->fPrevious==this->fCurrent)
  1051.             {
  1052.                     // Same item, don't draw this cycle.
  1053.                     // Delay one cycle to make it "flash".
  1054.                     // Unless the sysbeep is on, that will
  1055.                     // give enough delay to make it flash.
  1056.                 drawThisCycle = this->fBeep;
  1057.             }
  1058.                 // Clear our previous item, it was erased.
  1059.             this->fPrevious = 0;
  1060.         }
  1061.             // Auditory feedback and delay between erase and draw.
  1062.         if(this->fBeep)
  1063.         {
  1064.             SysBeep(1);
  1065.         }
  1066.             // Show.
  1067.         if(drawThisCycle)
  1068.         {
  1069.             this->fShow = false;
  1070.             this->fShowTime = TickCount();
  1071.             this->Draw(this->fCurrent, kPaint);
  1072.             this->fPrevious = this->fCurrent;
  1073.         }
  1074.     }
  1075.     else
  1076.     {
  1077.             // Nothing new to show.
  1078.             // Only erase previous if we have a positive interval.
  1079.         if(this->fPrevious && this->fInterval)
  1080.         {
  1081.             if(TickCount()>this->fShowTime+this->fInterval)
  1082.             {
  1083.                 // Counter timed out and no new stuff.
  1084.                 // Erase old stuff.
  1085.                     // Something is up there, erase it.
  1086.                 this->Draw(this->fPrevious, kErase);
  1087.                 this->fPrevious = 0;
  1088.             }
  1089.         }
  1090.     }
  1091. }
  1092.     
  1093.